package app

import (
	"context"

	v1 "gitee.com/liangziyisheng/caas-platform-mc/api/app/v1"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/consts"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/dao"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/model/do"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/model/entity"
	"gitee.com/liangziyisheng/caas-platform-mc/internal/service"

	"github.com/gogf/gf/v2/errors/gcode"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/glog"
)

type sApp struct{}

var l = g.Log("logger for App")

func New() *sApp {
	return &sApp{}
}

func init() {
	l.SetFlags(glog.F_FILE_LONG)
	service.RegisterApp(New())
}

func (s *sApp) List(ctx context.Context, in *v1.ListInput) (out *v1.ListOutput, err error) {
	md := dao.App.Ctx(ctx)
	if in.Name != "" {
		md = md.Where(dao.App.Columns().Name, in.Name)
	}
	if in.Type != "" {
		md = md.Where(dao.App.Columns().Type, in.Type)
	}
	out = &v1.ListOutput{}
	if err = md.Scan(&out.Data); err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.ListFailed, in))
	}
	return out, nil
}
func (s *sApp) Info(ctx context.Context, in *v1.InfoInput) (out *v1.InfoOutput, err error) {
	// 先查询app详情
	app := entity.App{}
	err = dao.App.Ctx(ctx).Where(dao.App.Columns().Id, in.Id).Scan(&app)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.InfoFailed, in))
	}
	out = &v1.InfoOutput{
		Description: app.Description,
		Id:          app.Id,
		Name:        app.Name,
	}

	// 查询组件列表
	err = dao.AppComponent.Ctx(ctx).Where(dao.AppComponent.Columns().AppCode, app.Code).Scan(&out.Components)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.InfoAppComponentFailed, app))
	}
	// 查询菜单及权限列表
	err = dao.AppMenu.Ctx(ctx).Where(dao.AppMenu.Columns().AppCode, app.Code).Scan(&out.Menus)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.InfoAppMenuFailed, app))
	}
	return out, nil
}

func (s *sApp) Add(ctx context.Context, in *v1.AddInput) (out *v1.AddOutput, err error) {
	l.Info(ctx, "Add")
	tx, err := g.DB().Begin(ctx)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.AddAppFailed, in))
	}
	id, err := tx.Model(dao.App.Table()).Data(in.App).InsertAndGetId()
	if err != nil {
		l.Error(ctx, err)
		tx.Rollback()
		return nil, gerror.NewCode(gcode.WithCode(consts.AddAppFailed, in))
	}
	// 创建顶级应用菜单
	menu := do.AppMenu{
		AppCode:     in.Code,
		Name:        in.Name,
		Description: in.Description,
		Sort:        1,
		Type:        consts.MenuTypes[0].Value,
		Path:        "/" + in.Name.(string),
	}
	_, err = tx.Model(dao.AppMenu.Table()).Data(menu).Insert()
	if err != nil {
		l.Error(ctx, err)
		tx.Rollback()
		return nil, gerror.NewCode(gcode.WithCode(consts.AddAppFailed, in))
	}
	out = &v1.AddOutput{Id: id}
	tx.Commit()
	return out, nil
}

func (s *sApp) Delete(ctx context.Context, in *v1.DeleteInput) (out *v1.DeleteOutput, err error) {
	// 先查询应用详情
	app := entity.App{}
	err = dao.App.Ctx(ctx).Where(dao.App.Columns().Id, in.Id).Scan(&app)
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.AppNotExist, in))
	}
	if app.Id == 0 {
		return nil, gerror.NewCode(consts.AppNotExist)
	}
	// 检查app是否被菜单引用
	var m []entity.AppMenu
	if err = dao.AppMenu.Ctx(ctx).Where(dao.AppMenu.Columns().AppCode, app.Code).Scan(&m); err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.QueryAppPermissionFailed, app.Code))
	}
	if len(m) > 0 {
		l.Error(ctx, "应用下关联了权限")
		return nil, gerror.NewCode(consts.PermissionAssignedInApplication)
	}

	// 检查app是否包含组件
	var c []entity.AppComponent
	if err = dao.AppComponent.Ctx(ctx).Where(dao.AppComponent.Columns().AppCode, app.Code).Scan(&c); err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.QueryAppComponentFailed, app.Code))
	}
	if len(c) > 0 {
		l.Error(ctx, "应用下关联了组件")
		return nil, gerror.NewCode(consts.AppComponentAssignedInApplication)
	}

	// 最终删除
	rs, err := dao.App.Ctx(ctx).Where(dao.App.Columns().Id, in.Id).Delete()
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.DeleteAppFailed, in.Id))
	}
	out = &v1.DeleteOutput{}
	if out.Rows, err = rs.RowsAffected(); err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.DeleteAppFailed, in.Id))
	}
	if out.Rows == 0 {
		return nil, gerror.NewCode(gcode.WithCode(consts.DeleteFailed, in.Id))
	}
	return out, nil
}

func (s *sApp) Update(ctx context.Context, in *v1.UpdateInput) (out *v1.UpdateOutput, err error) {
	out = &v1.UpdateOutput{}
	out.Rows, err = dao.App.Ctx(ctx).Data(in.App).Where(dao.App.Columns().Id, in.Id).UpdateAndGetAffected()
	if err != nil {
		l.Error(ctx, err)
		return nil, gerror.NewCode(gcode.WithCode(consts.UpdateAppFailed, in))
	}
	if out.Rows == 0 {
		return nil, gerror.NewCode(gcode.WithCode(consts.UpdateFailed, in.Id))
	}
	return out, nil
}
